package org.zjvis.datascience.service;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import io.minio.BucketExistsArgs;
import io.minio.GetObjectArgs;
import io.minio.ListObjectsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.Result;
import io.minio.messages.Item;
import okhttp3.OkHttpClient;

/**
 * @description Minio服务 Service
 * @date 2021-12-24
 */
@Service
public class MinioService {
	
	@Value("${minio.url}")
	private String httpUrl;
	
	@Value("${minio.secret}")
	private String key;
	
	@Value("${minio.password}")
	private String psw;
	
	private MinioClient minioClient;
	
	@PostConstruct
	private void init() throws KeyManagementException {
		OkHttpClient okHttpClient = MinioService.getUnsafeOkHttpClient();
		minioClient = MinioClient.builder()
		              .endpoint(httpUrl)
		              .credentials(key, psw)
				  				.httpClient(okHttpClient)
		              .build();
	}
	
	public InputStream getObject(String bucket, String objectName) throws Exception {
		return minioClient.getObject(GetObjectArgs.builder()
				  .bucket(bucket)
				  .object(objectName)
				  .build());
	}

	public InputStream getObject(String bucket, String objName, Long offset) throws Exception {
		return minioClient.getObject(GetObjectArgs.builder().bucket(bucket).object(objName).offset(offset).build());
	}


	public void putObject(String bucket, String objectName, InputStream is) throws Exception {
	    boolean existBucket = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucket).build());
	    if (!existBucket) {
	        minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
	    } else {
	        minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());
	    }
	    minioClient.putObject(PutObjectArgs.builder().bucket(bucket).object(objectName).stream(is, -1, 104857600).build());
	}
	
	public void deleteObject(String bucket, String objectName) throws Exception {
	    minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());
	}

	public void deleteObjects(String bucket,List<String> objectNames) throws Exception{
		for(String objectName:objectNames){
			minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(objectName).build());
		}
	}

	public List<String> listObjects(String bucket) throws Exception {
		List<Item> items = new ArrayList<>();
		Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).recursive(true).build());
		results.forEach(result -> {
			try {
				items.add(result.get());
			} catch (Exception e) {
			}
		});
		items.sort(new Comparator<Item>() {
			@Override
			public int compare(Item o1, Item o2) {
				return o2.lastModified().compareTo(o1.lastModified());
			}
		});
		return items.stream().map(Item::objectName).collect(Collectors.toList());
	}

	public List<String> listObjects(String bucket, String prefix) throws Exception {
	    List<Item> items = new ArrayList<>();
	    Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucket).recursive(true).prefix(prefix).build());
	    results.forEach(result -> {
          try {
			  items.add(result.get());
          } catch (Exception e) {
          } 
        });
		items.sort(new Comparator<Item>() {
			@Override
			public int compare(Item o1, Item o2) {
				return o2.lastModified().compareTo(o1.lastModified());
			}
		});
		return items.stream().map(Item::objectName).map(s->s.replaceFirst(prefix, "")).collect(Collectors.toList());
	}

	//取消SSL验证,解决SSLHandshakeException
	public static OkHttpClient getUnsafeOkHttpClient() throws KeyManagementException {
		try {
			final TrustManager[] trustAllCerts = new TrustManager[]{
					new X509TrustManager() {
						@Override
						public void checkClientTrusted(X509Certificate[] x509Certificates, String s) {}

						@Override
						public void checkServerTrusted(X509Certificate[] x509Certificates, String s) {}

						@Override
						public X509Certificate[] getAcceptedIssuers() {
							return new X509Certificate[]{};
						}
					}
			};

			final SSLContext sslContext = SSLContext.getInstance("SSL");
			sslContext.init(null, trustAllCerts, new SecureRandom());
			final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
			OkHttpClient.Builder builder = new OkHttpClient.Builder();
			builder.sslSocketFactory(sslSocketFactory, (X509TrustManager) trustAllCerts[0]);

			builder.hostnameVerifier((s, sslSession) -> true);
			return builder.build();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 将InputStream写入本地文件
	 * @param destination 写入本地目录
	 * @param input	输入流
	 * @throws IOException
	 */
	public static void writeToLocal(String destination, InputStream input)
			throws IOException {
		int index;
		byte[] bytes = new byte[1024];
		FileOutputStream downloadFile = new FileOutputStream(destination);
		while ((index = input.read(bytes)) != -1) {
			downloadFile.write(bytes, 0, index);
			downloadFile.flush();
		}
		downloadFile.close();
		input.close();
	}

	
	public static void main(String[] args) throws Exception {
	    MinioService minioService = new MinioService();
	    
	    minioService.httpUrl = "https://minio.zjvis.org";
	    minioService.key = "ZWUkouAYfWR5ycxc1LL8Mbp0d02yqDnrJHSKh0sN0javzWZ79KcIVY53ns78pCYW";
	    minioService.psw = "l5sKx2Bmuqv308ZtzkhaPSlgscUezpJrtBOH1mICnKCW6FfrCPZa6KDrV8zp5aAM#";
	    minioService.init();
	    
	    /*******Put***********/
//	    File f = new File("/Users/carson/seeds.csv");
//	    minioService.putObject("graph-file", String.format("carson/seeds_%s.csv", System.currentTimeMillis()), new FileInputStream(f));
	    
	    /*******List***********/
	    List<String> objs = minioService.listObjects("data-export-dengdazhen", "*");
	    System.out.println(objs);
	    
	    /*******Delete***********/
//	    minioService.deleteObject("graph-file", "carson/seeds_1621560619743.csv");
	    
	    /*******Read***********/
//	    InputStream is = minioService.getObject("graph-file", "carson/README.txt");
//	    String content = IOUtils.readInputStreamToString(is, Charset.forName("utf-8"));
//	    System.out.println(content);
	}

}
